package com.example.threadstack;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static com.example.threadstack.Utils.getCurrentTime;
import static com.example.threadstack.Utils.sleepForMs;

@RestController
@RequestMapping("wait-chain")
public class WaitChainController {
    private static final Logger LOGGER = LoggerFactory.getLogger(WaitChainController.class);

    private static final int CONCURRENT_COUNT = 100;
    private final LoadingCache<String, String> cache;

    public WaitChainController(){
        cache = CacheBuilder.newBuilder()
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String s) throws Exception {
                        sleepForMs(5000);
                        return getCurrentTime();
                    }
                });
    }

    private ExecutorService executorService = Executors.newCachedThreadPool(new BasicThreadFactory
            .Builder()
            .namingPattern("Wait-Chain-Thread-%d")
            .build()
    );

    @RequestMapping("future")
    public DeferredResult<String> future(){
        DeferredResult<String> result = new DeferredResult<>(10 * 1000L, "TimeOut");

        Future<String> future = this.executorService.submit(()->{
            sleepForMs(5000);
            return getCurrentTime();
        });

        CompletableFuture<String>[] futures = new CompletableFuture[CONCURRENT_COUNT];
        for (int i=0;i<CONCURRENT_COUNT;i++){
            futures[i] = CompletableFuture.supplyAsync(()->{
                try {
                    return future.get();
                }catch (Exception e){

                }
                return "ERROR";
            }, executorService);
        }

        CompletableFuture.allOf(futures).thenRun(()->result.setResult("SUCCESS"));

        return result;
    }

    @RequestMapping("guava-cache")
    public DeferredResult<String> guavaCache(){
        DeferredResult<String> result = new DeferredResult<>(10 * 1000L, "TimeOut");
        CompletableFuture<String>[] futures = new CompletableFuture[CONCURRENT_COUNT];
        for (int i=0;i<CONCURRENT_COUNT;i++){
            futures[i] = CompletableFuture.supplyAsync(this::loadFromGuava, executorService);
        }
        CompletableFuture.allOf(futures).thenRun(()->result.setResult("SUCCESS"));
        return result;
    }

    private String loadFromGuava() {
        return this.cache.getUnchecked(getCurrentTime());
    }

    @RequestMapping("logger")
    public DeferredResult<String> logger(){
        DeferredResult<String> result = new DeferredResult<>(10 * 1000L, "TimeOut");
        CompletableFuture<String>[] futures = new CompletableFuture[CONCURRENT_COUNT];
        for (int i=0;i<CONCURRENT_COUNT;i++){
            futures[i] = CompletableFuture.supplyAsync(this::writeLogger, executorService);
        }
        CompletableFuture.allOf(futures).thenRun(()->result.setResult("SUCCESS"));
        return result;
    }

    private String writeLogger(){
        for (int i = 0;i<10000;i++){
            LOGGER.info("{}", i);
        }
        return getCurrentTime();
    }

}
